package uebung03;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;

import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;

import Jama.Matrix;

import tools.Arrow;
import tools.ColorConversions;
import tools.Derivative;
import tools.Gauss;
import tools.ShowImage;

public class Aufgabe05wiki {
	public static final double gz = (1+Math.sqrt(5))/2.;
	public static void main(String[] args) {
		try {
			
			final BufferedImage t0 = ImageIO.read(Aufgabe05wiki.class.getClassLoader().getResourceAsStream("res/s0.png"));
			final BufferedImage t1 = ImageIO.read(Aufgabe05wiki.class.getClassLoader().getResourceAsStream("res/s1.png"));
			final int w = t0.getWidth(), h = t0.getHeight();
			
			final ArrayList<Point2D> origins = new ArrayList<Point2D>(), destinations = new ArrayList<Point2D>();
			final AffineTransform zoom = new AffineTransform();
			
			int[] pt0 = t0.getRGB(0, 0, w, h, new int[w*h], 0, w);
			int[] pt1 = t1.getRGB(0, 0, w, h, new int[w*h], 0, w);
			
			int[] lum0 = ColorConversions.getFastLuminance(pt0, new int[pt0.length]);
			int[] lum1 = ColorConversions.getFastLuminance(pt1, new int[pt0.length]);
			
			int[] t0dx = Derivative.dx(lum0, w, h, new int[lum0.length]);
			int[] t0dy = Derivative.dy(lum0, w, h, new int[lum0.length]);
			int[] t0dt = Derivative.dt(lum0, lum1, new int[lum0.length]);

			int[] lum = Derivative.prepareForShow(t0dt, new int[t0dt.length]);
			int[] pixels = ColorConversions.buildRGB(lum, lum, lum, new int[lum.length]);
			final BufferedImage dt = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
			dt.setRGB(0, 0, w, h, pixels, 0, w);
			
			int neighbours = 50;
			int length = 2*neighbours+1;
			int m = length*length, step = 5;
			Matrix idT = new Matrix(2, m), id = new Matrix(m, 2), it = new Matrix(m, 1);
			long start = System.nanoTime();
			for (int y = neighbours, endy = h-neighbours; y<endy; y++) {
				for (int x = neighbours, endx = w-neighbours; x<endx; x++) {
					if (x%step == 0 && y%step == 0) {
						int row = 0;
						for (int j = -neighbours; j<neighbours; j++) {
							for (int i = -neighbours; i<neighbours; i++) {
								double gauss = Gauss.gaussian1d(Math.sqrt(j*j+i*i), neighbours/3.);
								idT.set(0, row, t0dx[x+i+(y+j)*w]*gauss);
								idT.set(1, row, t0dy[x+i+(y+j)*w]*gauss);
	
								id.set(row, 0, t0dx[x+i+(y+j)*w]*gauss);
								id.set(row, 1, t0dy[x+i+(y+j)*w]*gauss);
								
								it.set(row, 0, -t0dt[x+i+(y+j)*w]*gauss);
								row++;
							}
						}

						Matrix S = idT.times(id);
						Matrix SI = idT.times(it);
//						System.out.println(S.getRowDimension()+" "+SI.getColumnDimension());
						Matrix uv = new Matrix(2,1);
						if (Double.compare(S.det(), 0)!=0)
						try {
							
							uv = S.solve(SI);
							double u = uv.get(0, 0), v = uv.get(1, 0);
							origins.add(new Point2D.Double(x,y));
							destinations.add(new Point2D.Double(x+u, y+v));
							
						}
						catch (RuntimeException re) {
	//						re.printStackTrace();
						}
					
//					System.out.println(uv.getRowDimension()+" "+uv.getColumnDimension());
					}
				}
			}
			long end = System.nanoTime();
			System.out.println("needed: "+(end-start)/1e9+"s");
			JFrame ff = new JFrame();
			ff.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			final ShowImage st0 = new ShowImage(t0, zoom), st1 = new ShowImage(t1, zoom);
			ff.add(st0, BorderLayout.WEST);
			ff.add(st1, BorderLayout.EAST);
			
			final JComponent canvas = new JComponent() {
				{setPreferredSize(new Dimension(w,h));}
				Arrow arrw = new Arrow();
				@Override
				public void paint(Graphics g) {
					Graphics2D g2 = (Graphics2D)g;
					g2.transform(zoom);
					g2.drawImage(dt, 0, 0, this);
					
					g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
					g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
					g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
					
					g2.setStroke(new BasicStroke(0.1f));
					
					for (int i = 0; i<origins.size(); i++) {
						arrw.setArrow(origins.get(i), destinations.get(i), 0.5, gz);
						g2.setColor(Color.magenta);
						g2.fill(arrw);
						g2.setColor(Color.GREEN);
						g2.draw(arrw);
					}
					super.paint(g);
				}
			};
			
			canvas.addMouseWheelListener(new MouseWheelListener() {

				@Override
				public void mouseWheelMoved(MouseWheelEvent e) {

					double scale = Math.pow(1.01, e.getWheelRotation());

					try {
						Point2D p = new Point2D.Double(e.getX(), e.getY());
						zoom.inverseTransform(p, p);
						zoom.concatenate(AffineTransform.getTranslateInstance(p.getX(), p.getY()));
						zoom.concatenate(AffineTransform.getScaleInstance(scale,scale));
						zoom.concatenate(AffineTransform.getTranslateInstance(-p.getX(), -p.getY()));

					} catch (NoninvertibleTransformException nte) {
						nte.printStackTrace();
					}
					canvas.repaint();
					st0.repaint();
					st1.repaint();
				}
			});
			
			ff.add(canvas, BorderLayout.CENTER);
			ff.pack();
			ff.setVisible(true);
//			for (int i = 0; i<origins.size(); i++) {
//				System.out.println(origins.get(i)+" ---> "+destinations.get(i));
//			}
		}
		catch (IOException iox) {
			iox.printStackTrace();
		}
		
	}
}
